iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 6
1


當我們咒語越變越多時,我們可以將常用的咒語包成「函式」(function),這樣一來,可以讓咒語變得更簡潔。另外,也可以使用Golang本身提供的函式,或者其他魔法使創造出來的函式

函式 func

函式什麼呢?簡單來說,就拿數學中的函數來舉例,比如有一個函式 f(x) = 2x + 3 當我給定 xf(x) 就會有我要的答案

數學上的函式主要都是數字和數字在玩,而魔法語言則更多元,可以是數字、字串、布林...等

直接舉個例子來看

實作一個函式,可以將浮點數取絕對值

package main
import "fmt"

// func [函式名](輸入值名稱 輸入值型態)輸出值型態

func abs(n float64) float64{
    if n >= 0 {
        return n    // 回傳 n
    }
    return -n       // 回傳 -n
}

func main(){
    // abs() 將回傳值傳給 fmt.Println
    fmt.Println(abs(10.0))
    fmt.Println(abs(-10.0))
}

執行結果
10
10

在一張基本的Golang牌中,一定會有 func main() 這個函式最先提供給魔仗使用,Golang的main()函式沒有接收任何值,所以 () 內是空的,又因為沒有回傳任何值所以 () 後也是空的。第一次接觸函式的魔法使們可能會覺得有點抽象,因此我會多舉幾個函式使用的例子

沒有回傳值的函式

函式不一定要有回傳值,函式可以只是一個動作,比如我們實作一個函式

實作一個函式輸入字串 someone,印出「someone啊,你快變成懲戒的鎖鏈」

package main
import "fmt"

func beChain(someone string){
    fmt.Println(someone + "啊,你快變成懲戒的鎖鏈")
}

func main(){
    beChain("風")
    beChain("水")
}

執行結果:
風啊,你快變成懲戒的鎖鏈!
水啊,你快變成懲戒的鎖鏈!


圖源:庫洛魔法使第一季第四集

初學者可能會對「有回傳值」和「沒回傳值」搞的很混亂。在沒有回傳值的函式中,可以想象成只是執行了某些行咒語,而有回傳值的函式則是除了執行這些咒語外還會把值帶回來給其他地方使用,比如給 fmt.Println() 使用,當然也可以把值帶給變數使用

沒有輸入值的函式

沒有規定函式一定要有輸入值

package main
import "fmt"

func release() string{
    return "隱藏著黑暗力量的鑰匙啊,在我面前顯示你真正的力量,跟你定下約定的小櫻命令你,封印解除!"
}

func main(){
    // release() 將回傳值傳給 fmt.Println
    fmt.Println(release())
}

執行結果:
隱藏著黑暗力量的鑰匙啊,在我面前顯示你真正的力量,跟你定下約定的小櫻命令你,封印解除!

2個以上輸入值的函式

當然函式也沒有規定你只能有一個輸入值

package main
import "fmt"

func release(power string, name string) string{
    return "隱藏著" + power + "力量的鑰匙啊,在我面前顯示你真正的力量,跟你定下約定的" + name + "命令你,封印解除!"
}

func main(){
    // release() 將回傳值傳給 fmt.Println
    fmt.Println(release("星星", "小櫻"))
}

執行結果
隱藏著星星力量的鑰匙啊,在我面前顯示你真正的力量,跟你定下約定的小櫻命令你,封印解除!

補充:星星鑰匙是第二季的內容

2個以上的回傳值

Golang牌比其他常見魔法牌猛的地方在他可以有多回傳值。這可以用來快速地實作錯誤處理。但目前進度還不會提。

package main
import "fmt"

func calc(a int, b int) (int , int){
    return a+b, a-b
}

func main(){
    sum, diff := calc(5, 2)
    fmt.Println(sum)
    fmt.Println(diff)
}

執行結果:
3
7

在等號左側放入接收回傳的變數,並用 , 做分隔即可實現多回傳值。其實在Golang中允許我們用一個等式去賦予多個變數值。第一次介紹變數是在#2 零與壹的魔法世界 ── 宣告數字型態的變數 | Golang魔法使做介紹,但因為篇幅關係所以沒有提到這個用法。

舉個例子比較好懂

package main
import "fmt"

func main(){
    a, b := 3, 10    // Golang 允許一次替兩個變數賦值
    fmt.Println(a, b)
}

執行結果:
3 10

有兩個回傳值但一個值暫時用不到該怎麼處理?
前面的章節有提到說在Golang中,如果宣告沒使用的變數魔仗(編譯器)會報錯,那如果我只要使用其中一個回傳值該怎麼辦?其實你只要將不使用的值的變數設成 _ 即可

package main
import "fmt"

func calc(a int, b int) (int , int){
    return a+b, a-b
}

func main(){
    sum, _ := calc(5, 2)
    fmt.Println(sum)
}

執行結果:
7

為回傳值命名

在Golang中,你甚至可以替回傳值命名。

func calc(a int, b int) (sum int , diff int){
    // 注意 sum 和 diff
    // 視為已經宣告,所以是用 = 不是 :=
    sum, diff = a+b, a-b
    return
}

要這樣寫也是沒問題的

func calc(a int, b int) (sum int , diff int){
    sum, diff = a+b, a-b
    return sum, diff
}

注意事項

  1. 在使用函式時,只要一觸發 return ,那麼後方的程式碼就不會繼續執行
  2. 有回傳值的函式不一定要去接收回傳值
  3. 函式中所宣告的變數有所謂的 scope,不會影響其他函數中所設的變數
  4. 有規定回傳值型態的函式,在函數最尾一定要使用 return
  5. 函式擺放的順序不重要

範例 1 & 2:reutrn 後的程式碼並不會執行, 有回傳值的函式不一定要去接收回傳值

package main
import "fmt"

func test(num int) int{
    if num > 10{
        fmt.Println("num > 10")
        return num                // 第 7 行
    }
    fmt.Println("num < 10")
    return num
}

func main(){
    test(100)    // 第 14 行
}

執行結果:
num > 10

很明顯可以看出,當魔仗執行到第 7 行後,就會跳回第 14 行,後方的程式碼就不會再執行了。只要在函式中遇到 return ,魔仗就不會繼續將後方的咒語解析。

另一方面,有回傳值(不管有幾個回傳值)的函數並沒有強制一定要有接收他的變數。

範例3:函式中所宣告的變數有所謂的 scope
在上一次教的迴圈中有提到scope,在迴圈內的變數並不可以在迴圈外使用,這件事同樣套用在 function 上

package main
import "fmt"

func test(num int){
    fmt.Println("in test()", num)
}

func main(){
    num := 10
    test(100)
    fmt.Println("in main()", num)
}

執行結果:
in test() 100
in main() 10

雖然 main()test() 中都是使用 num 但是兩者完全不會受到影響,這就是所謂的 scope,如此一來可以方便魔法使們更方便使用變數,使用時只要顧慮自己的 scope 即可

範例4:有規定回傳值型態的函式,在函數最尾一定要使用 return
許多魔法牌不會檢查這個錯誤,但是Golang魔仗會。首先我們先借用改造一下範例1的函式

大家懂了嗎?

範例5:函式擺放的順序不重要
你可能會覺得這不是屁話嗎?那你可大錯特錯!在一些魔法咒語中(如C),一定要先宣告才能呼叫,沒有宣告的函式是不可以呼叫的,但在 Golang 中則沒有這個限制

顛倒範例二的 func test()func main()

package main
import "fmt"

func main(){
    num := 10
    test(100)                      // 先呼叫使用 func test
    fmt.Println("in main()", num)
}

func test(num int){                // 後宣告 func test
    fmt.Println("in test()", num)
}

執行結果與範例二相同無異

後記

在某些魔法中如 C, Java, Dart 使用 function 時不使用 function 這個關鍵字,而是直接給定回傳型態和傳入型態

而某些魔法門派開始使用 function 來作為函式的關鍵字,但因為 function 有足足 8 個字母,所以各家魔法門派開始縮簡這個關鍵字,不過有時大家縮簡邏輯不太一樣,真的令人感到混亂

  • PHP, Javascript function (1995年)面市
  • Go func (2009年面市)
  • Kotiln fun (2011年面市)
  • Rust fn (2010年面市)
  • Python def (1991年面市)

如果以字數來看的話 Rust 大勝只用了僅僅 2 個字母,我先來預言一下,以後的魔法咒語說不定會直接以 f 來代替 function

本文多數圖片來自:


上一篇
#5 For 迴圈流程控制 | Golang魔法使
下一篇
#7 陣列 Array | Golang魔法使
系列文
Golang魔法使 ─ 30天從零開始學習Go語言 | 比Python還簡單 | 理科生一定學得會 | 文科生不好說30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言